/*
 * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.presentation.rmi;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;

import org.omg.CORBA.portable.ObjectImpl;

import java.io.ObjectStreamException;
import java.io.Serializable;

import com.sun.corba.se.spi.presentation.rmi.IDLNameTranslator;
import com.sun.corba.se.spi.presentation.rmi.PresentationManager;
import com.sun.corba.se.spi.presentation.rmi.DynamicStub;

import com.sun.corba.se.spi.orbutil.proxy.LinkedInvocationHandler;
import com.sun.corba.se.spi.orbutil.proxy.InvocationHandlerFactory;
import com.sun.corba.se.spi.orbutil.proxy.DelegateInvocationHandlerImpl;
import com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandler;
import com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl;
import java.security.AccessController;
import java.security.PrivilegedAction;

public class InvocationHandlerFactoryImpl implements InvocationHandlerFactory {

  private final PresentationManager.ClassData classData;
  private final PresentationManager pm;
  private Class[] proxyInterfaces;

  public InvocationHandlerFactoryImpl(PresentationManager pm,
      PresentationManager.ClassData classData) {
    this.classData = classData;
    this.pm = pm;

    Class[] remoteInterfaces =
        classData.getIDLNameTranslator().getInterfaces();
    proxyInterfaces = new Class[remoteInterfaces.length + 1];
    for (int ctr = 0; ctr < remoteInterfaces.length; ctr++) {
      proxyInterfaces[ctr] = remoteInterfaces[ctr];
    }

    proxyInterfaces[remoteInterfaces.length] = DynamicStub.class;
  }

  private class CustomCompositeInvocationHandlerImpl extends
      CompositeInvocationHandlerImpl implements LinkedInvocationHandler,
      Serializable {

    private transient DynamicStub stub;

    public void setProxy(Proxy proxy) {
      ((DynamicStubImpl) stub).setSelf((DynamicStub) proxy);
    }

    public Proxy getProxy() {
      return (Proxy) ((DynamicStubImpl) stub).getSelf();
    }

    public CustomCompositeInvocationHandlerImpl(DynamicStub stub) {
      this.stub = stub;
    }

    /**
     * Return the stub, which will actually be written to the stream.
     * It will be custom marshalled, with the actual writing done in
     * StubIORImpl.  There is a corresponding readResolve method on
     * DynamicStubImpl which will re-create the full invocation
     * handler on read, and return the invocation handler on the
     * readResolve method.
     */
    public Object writeReplace() throws ObjectStreamException {
      return stub;
    }
  }

  public InvocationHandler getInvocationHandler() {
    final DynamicStub stub = new DynamicStubImpl(
        classData.getTypeIds());

    return getInvocationHandler(stub);
  }

  // This is also used in DynamicStubImpl to implement readResolve.
  InvocationHandler getInvocationHandler(DynamicStub stub) {
    // Create an invocation handler for the methods defined on DynamicStub,
    // which extends org.omg.CORBA.Object.  This handler delegates all
    // calls directly to a DynamicStubImpl, which extends
    // org.omg.CORBA.portable.ObjectImpl.
    final InvocationHandler dynamicStubHandler =
        DelegateInvocationHandlerImpl.create(stub);

    // Create an invocation handler that handles any remote interface
    // methods.
    final InvocationHandler stubMethodHandler = new StubInvocationHandlerImpl(
        pm, classData, stub);

    // Create a composite handler that handles the DynamicStub interface
    // as well as the remote interfaces.
    final CompositeInvocationHandler handler =
        new CustomCompositeInvocationHandlerImpl(stub);

    AccessController.doPrivileged(new PrivilegedAction<Void>() {
      @Override
      public Void run() {
        handler.addInvocationHandler(DynamicStub.class,
            dynamicStubHandler);
        handler.addInvocationHandler(org.omg.CORBA.Object.class,
            dynamicStubHandler);
        handler.addInvocationHandler(Object.class,
            dynamicStubHandler);
        return null;
      }
    });

    // If the method passed to invoke is not from DynamicStub or its superclasses,
    // it must be from an implemented interface, so we just handle
    // all of these with the stubMethodHandler.  This used to be
    // done be adding explicit entries for stubMethodHandler for
    // each remote interface, but that does not work correctly
    // for abstract interfaces, since the graph analysis ignores
    // abstract interfaces in order to compute the type ids
    // correctly (see PresentationManagerImpl.NodeImpl.getChildren).
    // Rather than produce more graph traversal code to handle this
    // problem, we simply use a default.
    // This also points to a possible optimization: just use explict
    // checks for the three special classes, rather than a general
    // table lookup that usually fails.
    handler.setDefaultHandler(stubMethodHandler);

    return handler;
  }

  public Class[] getProxyInterfaces() {
    return proxyInterfaces;
  }
}
